#include "./cmd_uart.h"
#include "../device/uart.h"
#include <stdio.h>
#include <stdarg.h>

namespace driver {

using namespace device::uart;
using namespace utils;

using TX = PA<9>; // DMA1_Channel4
using TX_DMA = Dma<DMA1_BASE, 4>;
using TX_DMA_CHAN = DmaChan<DMA1_Channel4_BASE>;

using RX = PA<10>; // DMA1_Channel5
using RX_DMA = Dma<DMA1_BASE, 5>;
using RX_DMA_CHAN = DmaChan<DMA1_Channel5_BASE>;

static constexpr uint32_t MyBrr = 72000000 / 115200;

using MY_UART = Uart<USART1_BASE, MyBrr, TX, RX, TX_DMA, TX_DMA_CHAN, RX_DMA, RX_DMA_CHAN>;

CmdUart::CmdUart() {

    notify_write_complete();
    wait_ack_unlock();

    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN;
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    MY_UART::init(m_recv_buf, RECV_BUF_SIZE);

    NVIC_EnableIRQ(USART1_IRQn);
    NVIC_EnableIRQ(DMA1_Channel4_IRQn);
}

void CmdUart::write_by_dma(const void *buf, uint8_t len) {
    MY_UART::write(buf, len);
}

void CmdUart::write_raw(const void *buf, uint8_t len) {
    wait_write_complete();
    write_by_dma(buf, len);
}

void CmdUart::write_ack(uint8_t seq) {
    wait_write_complete();
    uint8_t len = conn::proto::make_ack(m_write_buf, seq);
    write_by_dma(m_write_buf, len);
}

void CmdUart::log(const char *format, ...) {

    wait_write_complete();
    va_list ap;
    va_start(ap, format);
    int n = vsprintf(reinterpret_cast<char *>(m_write_buf + 6), format, ap);
    encode<uint8_t>(m_write_buf + 5, n);
    uint8_t len = conn::proto::make_notify(m_write_buf, conn::notify::LOG, n + 1);
    write_by_dma(m_write_buf, len);
}

void CmdUart::on_recv_idle() {
    uint32_t index = RECV_BUF_SIZE - RX_DMA_CHAN::dntr();
    if (index == m_recv_index) {
        return;
    }
    utils::ByteView bw;
    if (index > m_recv_index) {
        bw.buf = m_recv_buf + m_recv_index;
        bw.len = index - m_recv_index;
        m_mq.post(bw);

    } else {
        bw.buf = m_recv_buf + m_recv_index;
        bw.len = RECV_BUF_SIZE - m_recv_index;
        m_mq.post(bw);

        bw.buf = m_recv_buf;
        bw.len = index;
        m_mq.post(bw);
    }
    m_recv_index = index;
}



}

using namespace driver;

extern "C"
void USART1_IRQHandler() {
    if (!MY_UART::is_idle()) {
        return;
    }
    MY_UART::clear_idle_flag();
    cmdUart->on_recv_idle();
}

extern "C"
void DMA1_Channel4_IRQHandler() {
    if (!TX_DMA::is_complete()) {
        return;
    }
    TX_DMA::clear_flags();
    cmdUart->notify_write_complete();
}

